<?php
// 应用公共文件
use think\exception\HttpResponseException;
use think\Response;

/**
 * 应用公共文件
 * ①统一数据返回格式
 * ②参数加密
 * ③参数解密
 * ④图片/文件上传
 *
*/


/**
 *  统一返回数据格式
 *  状态码    描述
 * 200    请求成功
 * 204    请求成功,未返回实体，比如option请求，
 * 400    错误的请求
 * 401    认证失败，这个一般在token验证那里
 * 403    拒绝访问
 * 404    请求的资源不存在
 * 422    参数验证错误
 * 500    服务器错误
 */
function result(string $msg = 'error', array $data = [], int $code = 200, string $type = 'json'): Response
{
    $result = [
        "code" => $code,
        "msg" => $msg,
        "data" => $data
    ];
    // 调用Response的create方法，指定code可以改变请求的返回状态码
    return Response::create($result, $type)->code($code);
}

/**
 * 返回封装后的 API 数据到客户端
 * @access protected
 * @param mixed $msg 提示信息
 * @param mixed $data 要返回的数据
 * @param int $code 错误码，默认为0
 * @param string $type 输出类型，支持json/xml/jsonp
 * @param array $header 发送的 Header 信息
 * @return void
 * @throws HttpResponseException
 * 状态码    描述
 * 200    请求成功
 * 204    请求成功,未返回实体，比如option请求，
 * 400    错误的请求
 * 401    认证失败，这个一般在token验证那里
 * 403    拒绝访问
 * 404    请求的资源不存在
 * 422    参数验证错误
 * 500    服务器错误
 */
function suc($msg, $data = null, $code = 200)
{
    $time = time();
    $result = [
        'time' => $time,
        'key' => $data,
    ];

    if (!empty($data)) {
        $result['key'] = reqEncode($data, $time);
    }
    return result($msg, $result, $code);
}

/**
 * 请求参数加密
 * openssl_decrypt
 * @param $encrypt_key  密钥
 * @param $iv 初始化向量 长度根据加密方式来确定
 */
function reqEncode($data, $ekey)
{
    $key = \config('req_key');
    $ekey = $ekey . $key['admin_key'];
    return openssl_encrypt(json_encode($data), "AES-128-CBC", $ekey, 0, $key['admin_iv']);
}

/**
 *  请求参数解密
 * openssl_decrypt
 * @param $encrypt_key  密钥
 * @param $iv 初始化向量
 */
function reqDecode()
{
    $params = request()->param();//请求获取的参数
    if (!empty($params)) {
        $key = \config('req_key');
        $ekey = $params['time'] . $key['admin_key'];
        $data = openssl_decrypt($params['key'], "AES-128-CBC", $ekey, 0, $key['admin_iv']);
        return json_decode($data, true);
    }
    return false;
}

/**
 * 后台处理 图片上传
 * 上传至本地服务器
 * @param $file 文件对象
 * @param $path 文件存储路径
 */
function uploadImage($path)
{
    // 获取上传文件
    $files = request()->file();
    //校验是否有非法图片上传
    $ckeck = check_illegal($files['file']);
    if(!$ckeck){
        return suc('非法图片','',1);
    }
    try {
        validate(['image'=>'fileSize:10240|fileExt:jpg,png,jpeg'])
            ->check($files); //验证类对上传文件的验证，包括文件大小、文件类型和后缀
        $savename = '';
        foreach($files as $file) {
            $savename= \think\facade\Filesystem::disk('public')->putFile( $path,$file,'md5'); //默认根据日期和微秒数生成文件命名规则。这里指定上传文件的命名规则 md5
            $savename = request()->domain().'/storage/'.$savename;
        }
        return suc('上传成功',$savename);
    } catch (\think\exception\ValidateException $e) {
        return suc($e->getMessage(),'',1);
    }
}

/**
 * 检测上传图片是否包含有非法代码
*/
function check_illegal($image)
{
    if (file_exists($image)) {
        $resource = fopen($image, 'rb');
        $fileSize = filesize($image);
        fseek($resource, 0);
        if ($fileSize > 512) { // 取头和尾
            $hexCode = bin2hex(fread($resource, 512));
            fseek($resource, $fileSize - 512);
            $hexCode .= bin2hex(fread($resource, 512));
        } else { // 取全部
            $hexCode = bin2hex(fread($resource, $fileSize));
        }
        fclose($resource);
        /* 匹配16进制中的 <% ( ) %> */
        /* 匹配16进制中的 <? ( ) ?> */
        /* 匹配16进制中的 <script | /script> 大小写亦可*/
        if (preg_match("/(3c25)|(3c3f.*?706870)|(3C534352495054)|(2F5343524950543E)|(3C736372697074)|(2F7363726970743E)/is", $hexCode)) {
            return false;
        }
    }
    return true;
}